#! /usr/bin/python
import sys
import os
import re
import backupManager.config as config
from backupManager.enum import AEnums
import math
import datetime
import Pyro4
import hashlib
import traceback


host = ''
port = config.pyro_port


class indexServer(object):
    def __init__(self):
        pass

    def index(self, commDict):
        return indexDrive(commDict)

def compileValidExt(validext):
    if validext:
        validext = '|'.join(validext)
        validext = "\.(" + validext + ")$"
        validext = re.compile(str(validext), re.IGNORECASE)
    else:
        validext = None
    return validext

def compileSkipList(skip):
    skip = '|'.join(skip)
    if skip:
        skip = re.compile(str(skip), re.IGNORECASE)
    else:
        skip = None
    return skip


def indexDrive(commDict):
    print 'Received index request', str(datetime.datetime.now()), commDict
    if not os.path.exists(commDict[AEnums.REMOTEPATH]):
        return AEnums.NOTEXIST
    validext = compileValidExt(commDict[AEnums.VALIDEXTS])
    skip = compileSkipList(commDict[AEnums.SKIPLIST])
    root = []
    files = []
    # noinspection PyBroadException
    try:
        for path in commDict[AEnums.PATHS]:
            path = path.replace(commDict[AEnums.SEP], os.sep)
            path = unicode(path)
            newFile = index(path, validext, skip, files, root=True, commDict=commDict)
            files.append(newFile)
            root.append(newFile[AEnums.ID])
    except:
        exc_type, exc_value, exc_traceback = sys.exc_info()
        traceback.print_exception(exc_type, exc_value, exc_traceback, limit=2, file=sys.stdout)
    print 'Done indexing!', str(datetime.datetime.now())
    return {
        AEnums.FILES: files,
        AEnums.ROOT: root,
    }

def createDirRef(path, dirName, root, commDict):
    fd = {
        AEnums.NAME: os.path.basename(dirName),
        AEnums.PATH: os.path.dirname(os.path.join(commDict[AEnums.REMOTEPATH], path)).replace(
            commDict[AEnums.REMOTEPATH], '').replace(
                os.sep, commDict[AEnums.SEP]),
        AEnums.PREFIX: commDict[AEnums.NAME] + ':',
        AEnums.DIR: True,
        AEnums.VOLNAME: commDict[AEnums.NAME],
        AEnums.SERIAL: commDict[AEnums.SERIAL],
        AEnums.SIZE: 0,
        AEnums.MTIME: 0,
        AEnums.PARENT: '',
        AEnums.SETNAME: commDict[AEnums.SETNAME],
    }
    m = hashlib.md5()
    m.update(fd[AEnums.NAME].encode('UTF-16'))
    m.update(fd[AEnums.PATH].encode('UTF-16'))
    m.update(fd[AEnums.VOLNAME])
    m.update(fd[AEnums.SERIAL])
    m.update(fd[AEnums.SETNAME])
    if root:
        m.update(AEnums.ROOT)
    fileID = m.hexdigest()
    fd[AEnums.ID] = fileID
    return fd

def createFileRef(path, dirName, foundFile, commDict):
    mtime = int(math.floor(os.path.getmtime(os.path.join(dirName, foundFile))))
    if mtime % 2:
        mtime += 1
    newFile = {
        AEnums.NAME: foundFile,
        AEnums.PATH: path.replace(os.sep, commDict[AEnums.SEP]),
        AEnums.PREFIX: commDict[AEnums.NAME] + ':',
        AEnums.DIR: False,
        AEnums.SIZE: float(os.path.getsize(os.path.join(dirName, foundFile))) / float(1024),
        AEnums.MTIME: mtime,
        AEnums.VOLNAME: commDict[AEnums.NAME],
        AEnums.SERIAL: commDict[AEnums.SERIAL],
        AEnums.SETNAME: commDict[AEnums.SETNAME],
    }
    m = hashlib.md5()
    m.update(newFile[AEnums.NAME].encode('UTF-16'))
    m.update(newFile[AEnums.PATH].encode('UTF-16'))
    m.update(newFile[AEnums.VOLNAME])
    m.update(newFile[AEnums.SERIAL])
    m.update(newFile[AEnums.SETNAME])
    fileID = m.hexdigest()
    newFile[AEnums.ID] = fileID
    return newFile

def shouldProcess(dirName, foundFile, skip, ext):
    if not os.access(os.path.join(dirName, foundFile), os.R_OK):
        return False
    if skip:
        if re.search(skip, foundFile):
            return False
    if not os.path.isdir(os.path.join(dirName, foundFile)):
        if ext:
            if not re.search(ext, foundFile):
                return False
    return True


def getIndexFiles(dirName):
    # noinspection PyBroadException
    try:
        files = os.listdir(unicode(dirName))
    except:
        exc_type, exc_value, exc_traceback = sys.exc_info()
        traceback.print_exception(exc_type, exc_value, exc_traceback, limit=2, file=sys.stdout)
        return None
    return files

def index(path, validext, skip, filesIn, root=False, commDict=None):
    """

    :param path: directory path to index
    :param validext: list of valid extensions to look for
    :param skip: list of regular expressions to skip
    :param filesIn: list object to append new files to
    :param root: is this a root directory?
    :param commDict: a dictionary filled with path information (i.e. local -> remote mapping)
    :return: a file descriptor representing the directory
    """
    dirName = os.path.join(commDict[AEnums.REMOTEPATH], path)
    while re.search(r'/$', dirName, re.IGNORECASE):
        dirName = re.sub(r'/$', '', dirName)
    if not os.path.isdir(dirName):
        return None
    files = getIndexFiles(dirName)
    fd = createDirRef(path, dirName, root, commDict)
    passedFiles = []
    for foundFile in files[:]:
        if not shouldProcess(dirName, foundFile, skip, validext):
            continue
        if os.path.isdir(os.path.join(dirName, foundFile)):
            newFile = index(os.path.join(path, foundFile), validext, skip, filesIn, False, commDict)
        else:
            newFile = createFileRef(path, dirName, foundFile, commDict)
        if newFile:
            newFile[AEnums.PARENT] = fd[AEnums.ID]
            passedFiles.append(newFile)
    if not passedFiles and not root:
        return None
    [filesIn.append(x) for x in passedFiles]
    return fd


def main():
    i = indexServer()
    Pyro4.Daemon.serveSimple(
        {
            i: "anybackup.indexserver"
        },
        ns=False, port=port, host='')

if __name__ == "__main__":
    main()
